/**
 * HTML5 드래그 드롭.
 * 드래그할 엘리먼트들(A)과 드롭대상 엘리먼트들(B)을 지정하여,
 * A가 B 위에 올라가면 A를 B의 제일 마지막에 넣는다.
 *  
 * javascript:HashMap 객체가 반드시 필요함.
 * 2012-03-05일 현재, 크롬 , 파이어폭스에서만 지원.
 * 크롬에서는 e.dataTransfer.getData('text') <- 여기서 text 대신 다른 값은 인식하지 못하는 버그가 있음.
 * 크롬에서 e.dataTransfer.getData('text') 를 제대로 지원하지 않아, 이동중에 다른 효과를 주는 것은 미적용.
 * 향후 개선방안.
 * 1. 크롬에서 지원한다면 dragOverHandler 를 개선하도록.
 * 2. 현재는 드래그 해서 무조건 뒤로 집어넣는데, 마우스 좌표에 따라 앞에 들어갈 수도 있어야 한다.
 * 3. 지금은 드래그 해서 드롭타켓에 집어넣는 방식인데, 자유롭게 좌표에 따라 이동하는 방식도 추가.
 * 
 * @author BJS aka Zephyr
 * @version 0.5
 * @date 2012-03-08
 */
var DragDrop = function() {
	this.dragElementList = new HashMap();
	this.dropTargetList  = new HashMap();
};

DragDrop.prototype = {
	/**
	 * 드래그할 element 를 등록한다
	 * 
	 * @param    dragElementId : 실제로 드래그 되어 움직일 대상의 ID값.(유일해야 하므로 ID)
	 * @param    dragHandlerSelector : 위 대상을 드래그 하기 위한 영역을 querySelector 로 지정.
	 * @param -- dragHandlerSelector를 마우스로 끌면 dragElementId를 이동하는 방식으로 동작함.
	 * @param -- dragHandlerSelector 가 없으면 dragElementId 가 자체를 끌어서 이동하게 된다.
	 * 
	 * 
	 */
	addDragELement : function(dragElementId , dragHandlerSelector) {
		var dragElement = document.querySelector('#'+dragElementId);
		var dragHandler = dragElement.querySelector('#' + dragElementId + ' ' + dragHandlerSelector);
		if(dragElement) {
			if( dragHandler ) {
				this.dragElementList.put(dragElementId, { element : dragElementId , handler : '#' + dragElementId + ' ' + dragHandlerSelector });
				$( dragHandler ).css('cursor' , 'move');
			}
			else {
				this.dragElementList.put(dragElementId, { element : dragElementId , handler : '#' + dragElementId });
				$( dragElement ).css('cursor' , 'move');
			}
		}
	},
	/**
	 * 아이템들이 드랍될 element를 등록한다.
	 * @param id
	 */
	addDropTarget : function(id) {
		var dropElement = document.querySelector('#'+id); 
		if(dropElement) {
			this.dropTargetList.put(id, dropElement);
		}
	},
	/**
	 * 해당 element의 드래그드롭을 중지한다.
	 * @param id
	 */
	remove: function(id) {
		this.dragElementList.remove(id);
		this.dropTargetList.remove(id);
	},
	/**
	 * 드래그 드롭을 시작한다.
	 */
	run: function() {
		var key;
		for( var idx in this.dragElementList.keySet() ) {
			key = this.dragElementList.keySet()[idx];
			this.draggable( key , this.dragElementList.get( key ) );
		}
		for( var idx in this.dropTargetList.keySet() ) {
			key = this.dropTargetList.keySet()[idx];
			this.dropable( key , this.dropTargetList.get( key ) );
		}
	},

	draggable: function(id , element) {
		var _this = this;
		var dragHandler = document.querySelector(element.handler);
		if(dragHandler) {
			dragHandler.setAttribute('draggable' , 'true');
			dragHandler.addEventListener('dragstart' , function(e){
				_this.dragstartHandler(e , id);
			});
		}
	},
	dropable: function(id , element) {
		var _this = this;
		if(element) {
			element.addEventListener('dragover' , function(e){
				_this.dragoverHandler(e , _this);
			} , false);
			element.addEventListener('drop' , function(e) {
				_this.dropHandler(e , element , _this);
			} , false);
		}
	},
	/**
	 * dragStart 시 이벤트 핸들러.
	 * 동작을 바꾸고 싶다면 이 핸들러를 대체하여 사용.
	 * 사용 방법.
	 * var n_dd = new DragDrop();
	 * n_dd.dragstartHandler = function() {...}
	 * 
	 * @param e
	 * @param id
	 */
	dragstartHandler: function(e , id) {
		var dragElement = $('#' + id);
		
		e.dataTransfer.setData('text' , id  );
				
	},
	dragoverHandler: function(e , _this) {
		if( _this.dragElementList.containsKey( e.dataTransfer.getData('text') ) ) {
			e.preventDefault(); // 크롬에서는 되지 않는다.
		}
		
		e.preventDefault();
	},
	dropHandler: function(e , element , _this) {
		var dragElementId = e.dataTransfer.getData('text');
		
		var mx =  e.pageX;
		var my = e.pageY;
		var dragElement = null;
		if( _this.dragElementList.containsKey( dragElementId ) ) {
			if (e.stopPropagation) e.stopPropagation(); // stops the browser from redirecting...why???
			dragElement = $( '#' +  dragElementId);
		//	element.appendChild( document.querySelector( '#' +  dragElementId) );
		}
		if(dragElement == null) return;
		
		var elemId , elem , bx , by; // -1 : 왼쪽&위 , 0 : 내부 , 1 : 오른쪽&아래
		for( var idx in _this.dragElementList.keySet() ) {
			elemId = _this.dragElementList.keySet()[idx];
			elem = $('#' + elemId);
			
			if( dragElementId == elemId ) {
				continue;
			}
			
			bx = -100 , by = -100;
			if( mx < elem.offset().left ) bx = -1; 
			else if( mx <= elem.offset().left + elem.width() ) bx = 0;
				
			else if( elem.offset().left + elem.width() < mx ) bx = 1;
			
			if( my < elem.offset().top ) by = -1;
				
			else if( my <= elem.offset().top + elem.height() ) by = 0;
				
			else if( elem.offset().top + elem.height() < my ) by = 1;
			
			var iElem;
			if( bx == 0 ) {
				if(by == 0 ) {
					console.log(elem);
					console.log(bx + ',' +by);
					dragElement.insertAfter( elem );
					return;
				}
				else if( by == -1 ) {
					dragElement.insertBefore( elem );
					return;
				}
				else if( by == 1 ) {
					iElem = elem;
					continue;
				}
			}
			if(iElem) dragElement.insertAfter( iElem );
			
		}
		
		for( var idx in _this.dropTargetList.keySet() ) {
			elemId = _this.dropTargetList.keySet()[idx];
			elem = $('#' + elemId);
			
			bx = -100 , by = -100;
			if( mx < elem.offset().left ) bx = -1; 
			else if( mx <= elem.offset().left + elem.width() ) bx = 0;
				
			else if( elem.offset().left + elem.width() < mx ) bx = 1;
			
			if( my < elem.offset().top ) by = -1;
				
			else if( my <= elem.offset().top + elem.height() ) by = 0;
				
			else if( elem.offset().top + elem.height() < my ) by = 1;
			
			if( bx == 0 && by == 0 ) {
				elem.append( dragElement );
				return;
			}
		}
	}
};